﻿/**************************************************************************
创建日期:	2019/7/16 14:38:25     
作	  者:	张存
邮 	  箱:	zhangcunliang@126.com
创建时间:	
Copyright (c)  ZhCun.cn

描	述： 参考 https://blog.csdn.net/a123_z/article/details/94011395 
         1.对所有http请求记录日志,对指定地址进行 response 内容记录；
         2.对所有ajax（webapi）请求进行加解密操作
           如果 _IsEncrypt = true  那么对未进行加密处理的返回400错误（请求内容置空）；
           如果 _IsEncrypt = false 那么加入密钥的进行加解密，未填写密钥的不进行加解密处理；
           非ajax（页面）请求，不对 Request 与 response 进行加解密；
记	录： 2019.7.19 暂时不使用该中间件进行记录日志与加解密，静态页面同样会触发
***************************************************************************/
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using ZhCun.Utils;
using ZhCun.Utils.Helpers;
using ZhCun.WebCommon.AL;
using ZhCun.WebCommon.DBModels;

namespace ZhCun.WebCommon
{
    /// <summary>
    /// 负责记录日志和加密的中间件(不使用，加解密 这块不能用)
    /// </summary>
    public class CommonMiddleware
    {
        static CommonMiddleware()
        {
            _WriteHttpLog = AppCommonConfig.AppSettings.WriteHttpLog;
            if (!string.IsNullOrWhiteSpace(AppCommonConfig.AppSettings.RsaPrivateKey))
            {
                _RSA = new RSAHelper(AppCommonConfig.AppSettings.RsaPrivateKey);
            }
            _IsEncrypt = AppCommonConfig.AppSettings.DataEncrypt;
        }
        private readonly static bool _WriteHttpLog;
        /// <summary>
        /// RSA加密对象(只针对ajax)
        /// </summary>
        private readonly static RSAHelper _RSA;
        /// <summary>
        /// 是否必须为ajax请求加密
        /// </summary>
        private readonly static bool _IsEncrypt;

        private readonly RequestDelegate _next;

        public CommonMiddleware(RequestDelegate next)
        {
            this._next = next;
        }
        /// <summary>
        /// 根据URL判断是否需要数据加密，开启加密后
        /// </summary>
        bool IsTextRequest(HttpContext context)
        {
            if (RegexHelper.Check(context.Request.Path.Value, AppCommonConfig.AppSettings.DataEncryptUrlIgnore))
            {
                return false;
            }
            if (RegexHelper.Check(context.Request.Path.Value, AppCommonConfig.AppSettings.DataEncryptUrlMatch))
            {
                return true;
            }
            return false;
        }

        public async Task Invoke(HttpContext context /* other dependencies */)
        {
            if (RegexHelper.Check(context.Request.Path.Value, "[//s//S]*(.js)|(.css)$"))
            {
                //过滤静态文件
                await _next.Invoke(context);

                return;
            }

            if (!_WriteHttpLog && _RSA == null)
            {
                //不写日志，未配置加解密参数，进行默认处理
                await _next.Invoke(context);

                return;
            }
            TSysLogHttp logObj = null;
            Stopwatch watch = null;
            if (_WriteHttpLog)
            {
                logObj = new TSysLogHttp();
                logObj.RequestPath = context.Request.Path.Value;
                logObj.HttpType = 0;
                //logObj.AddHost = context.GetRemoteIp();
                logObj.HttpMethod = context.Request.Method;
                logObj.RequestParam = context.Request.QueryString.ToString();
                var token = context.GetHeader(Constants.TOKEN_NAME);
                if (string.IsNullOrWhiteSpace(token))
                    token = context.GetCookie(Constants.TOKEN_NAME);
                if (!string.IsNullOrWhiteSpace(token))
                {
                    var tokenObj = LoginStateCache.Get(token);
                    logObj.AddUserId = tokenObj?.LoginUserId;
                    logObj.UserName = tokenObj?.UserName;
                }
                watch = new Stopwatch();
                watch.Start();
            }

            var request = context.Request.Body;
            var response = context.Response.Body;

            MemoryStream newRequest = null, newResponse = null;
            StreamReader readerRequest = null, readerResponse = null;
            StreamWriter writerRequest = null, writerResponse = null;
            string requestBody = null;
            string responseBody = null;

            try
            {
                string aesKey = null;
                bool isTextRequest = IsTextRequest(context);
                var aesEncryptKey = context.GetHeader(Constants.DATA_ENCRYPT_KEY_VALUE);
                if (string.IsNullOrWhiteSpace(aesEncryptKey))
                {
                    if (_IsEncrypt && isTextRequest)
                    {
                        context.Response.Clear();
                        context.Response.StatusCode = 400;
                        await context.Response.WriteAsync("this data must be encrypted!");
                        return;
                    }
                }
                else
                {
                    aesKey = _RSA.Decrypt(aesEncryptKey);
                }

                if (_WriteHttpLog || !string.IsNullOrEmpty(aesKey))
                {

                    if (context.Request.ContentLength > 0)
                    {
                        //密钥不为空，或者需要写日志，都应该读RequestBody
                        newRequest = new MemoryStream();
                        request.CopyTo(newRequest);
                        readerRequest = new StreamReader(newRequest);
                        newRequest.Position = 0;
                        requestBody = await readerRequest.ReadToEndAsync();

                        if (!string.IsNullOrEmpty(aesKey) && !string.IsNullOrEmpty(requestBody))
                        {
                            requestBody = DEncryptAES.Decrypt(requestBody, aesKey);
                            writerRequest = new StreamWriter(newRequest);
                            newRequest.Position = 0;
                            await writerRequest.WriteAsync(requestBody);
                            await writerRequest.FlushAsync();
                        }
                        newRequest.Position = 0;
                        context.Request.Body = newRequest;
                    }
                    if (isTextRequest)
                    {
                        //只有文本请求才读取和加解密 Response
                        newResponse = new MemoryStream();
                        context.Response.Body = newResponse;

                        await _next(context);

                        if (context.Response.ContentLength > 0)
                        {
                            readerResponse = new StreamReader(newResponse);
                            newResponse.Position = 0;
                            responseBody = await readerResponse.ReadToEndAsync();
                            if (!string.IsNullOrEmpty(aesKey) && !string.IsNullOrEmpty(responseBody))
                            {
                                var enResult = DEncryptAES.Encrypt(responseBody, aesKey);
                                writerResponse = new StreamWriter(newResponse);
                                newResponse.Position = 0;
                                await writerResponse.WriteAsync(enResult);
                                await writerResponse.FlushAsync();
                            }
                        }
                        newResponse.Position = 0;
                        newResponse.CopyTo(response);
                        context.Response.Body = response;
                        newResponse.Dispose();
                        return; //这个return 很重要；
                    }
                }

                await _next(context);
            }
            catch (Exception ex)
            {
                LogHelper.LogObj.Error("中间件发生异常", ex);
                responseBody = "中间件发生异常，" + ex.Message;
                //context.Response.Clear();
                //context.Response.StatusCode = 500;
                //await context.Response.WriteAsync("");
                throw ex;
            }
            finally
            {
                if (newResponse != null) newResponse.Dispose();
                if (newRequest != null) newRequest.Dispose();
                if (request != null) request.Dispose();
                if (response != null) response.Dispose();
                if (readerRequest != null) readerRequest.Dispose();
                if (readerResponse != null) readerResponse.Dispose();
                if (writerRequest != null) writerRequest.Dispose();
                if (writerResponse != null) writerResponse.Dispose();

                // 响应完成时存入缓存
                context.Response.OnCompleted(() =>
                {
                    if (_WriteHttpLog)
                    {
                        watch.Stop();
                        logObj.ResponseContent = responseBody;
                        logObj.RequestContent = requestBody;
                        logObj.StatusCode = context.Response.StatusCode;
                        logObj.RunTime = (decimal)watch.Elapsed.TotalSeconds;
                        DBLog.WriteLogHttp(logObj);
                    }
                    return Task.CompletedTask;
                });
            }
        }
    }
}